import {Action as ReduxAction, combineReducers, Reducer} from 'redux';

// Redux State
interface LoadingState {
  [loading: string]: number;
}

export interface State {
  loading: LoadingState;
  app: object;
}

// Redux Action
const SET_STATE_ACTION = '@@framework/setState';

export interface Action<P> extends ReduxAction<string> {
  payload: P;
  name?: typeof SET_STATE_ACTION;
}

// Redux Action: SetState (to update state.app)
interface SetStateActionPayload {
  module: string;
  state: any;
}

// state must be complete module state, not partial
export function setStateAction(
  module: string,
  state: object,
  type: string,
): Action<SetStateActionPayload> {
  return {
    type,
    name: SET_STATE_ACTION,
    payload: {module, state},
  };
}

function setStateReducer(
  state: State['app'] = {},
  action: Action<any>,
): State['app'] {
  // Use action.name for set state action, make type specifiable to make tracking/tooling easier
  if (action.name === SET_STATE_ACTION) {
    const {module, state: moduleState} =
      action.payload as SetStateActionPayload;
    return {...state, [module]: moduleState};
  }
  return state;
}

// Redux Action: Loading (to update state.loading)
interface LoadingActionPayload {
  identifier: string;
  show: boolean;
}

export const LOADING_ACTION = '@@framework/loading';

export function loadingAction(
  show: boolean,
  identifier: string = 'global',
): Action<LoadingActionPayload> {
  return {
    type: LOADING_ACTION,
    payload: {identifier, show},
  };
}

function loadingReducer(
  state: LoadingState = {},
  action: Action<LoadingActionPayload>,
): LoadingState {
  if (action.type === LOADING_ACTION) {
    const payload = action.payload as LoadingActionPayload;
    const count = state[payload.identifier] || 0;
    return {
      ...state,
      [payload.identifier]: count + (payload.show ? 1 : -1),
    };
  }
  return state;
}

// Root Reducer
export function rootReducer(): Reducer<State> {
  return combineReducers<State>({
    loading: loadingReducer,
    app: setStateReducer,
  });
}

// Helper function, to determine if show loading
export function showLoading(state: State, identifier: string = 'global') {
  return state.loading[identifier] > 0;
}
